//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-11-07     ZhangYihua   first version
//
// Description  : 
//################################################################################

module axi2apb_brg #(
parameter           ADDR_BW                 = 16,
parameter           DATA_BW                 = 32
) ( 
input                                       rst_n,
input                                       clk,

// AXI-lite
input               [ADDR_BW-1:0]           s_axi_awaddr,
input                                       s_axi_awvalid,
output                                      s_axi_awready,

input               [DATA_BW-1:0]           s_axi_wdata,
input                                       s_axi_wvalid,
output                                      s_axi_wready,

output              [2-1:0]                 s_axi_bresp,
output                                      s_axi_bvalid,
input                                       s_axi_bready,

input               [ADDR_BW-1:0]           s_axi_araddr,
input                                       s_axi_arvalid,
output                                      s_axi_arready,

output              [DATA_BW-1:0]           s_axi_rdata,
output              [2-1:0]                 s_axi_rresp,
output                                      s_axi_rvalid,
input                                       s_axi_rready,

// APB 3.0
output  reg                                 psel,
output  reg                                 penable,
output  reg                                 pwrite,
output  reg         [ADDR_BW-1:0]           paddr,
output  reg         [DATA_BW-1:0]           pwdata,
input               [DATA_BW-1:0]           prdata,
input                                       pready
);

//################################################################################
// define local varialbe and localparam
//################################################################################
wire                                        arvalid;
wire                [ADDR_BW-1:0]           araddr;
wire                                        arready;
wire                                        awvalid;
wire                [ADDR_BW-1:0]           awaddr;
wire                                        awready;
wire                                        wvalid;
wire                [DATA_BW-1:0]           wdata;
wire                                        wready;
wire                                        ar_req;
wire                                        aw_req;
wire                                        idle;
wire                                        sch_en;
wire                                        apb_end;
reg                                         bvalid;
wire                                        bready;
reg                                         rvalid;
reg                 [DATA_BW-1:0]           rdata;
wire                                        rready;

//################################################################################
// main
//################################################################################

reg_slicer #(
        .RS_MODE                        ("BID_RS_HALF_RATE"             ),	// timing optimization for all input, but half rate pipeline
        .DAT_BW                         (ADDR_BW                        )
) u_araddr ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .src_vld                        (s_axi_arvalid                  ),
        .src_dat                        (s_axi_araddr                   ),
        .src_rdy                        (s_axi_arready                  ),

        .dst_vld                        (arvalid                        ),
        .dst_dat                        (araddr                         ),
        .dst_rdy                        (arready                        )
);

reg_slicer #(
        .RS_MODE                        ("BID_RS_HALF_RATE"             ),	// timing optimization for all input, but half rate pipeline
        .DAT_BW                         (ADDR_BW                        )
) u_awaddr ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .src_vld                        (s_axi_awvalid                  ),
        .src_dat                        (s_axi_awaddr                   ),
        .src_rdy                        (s_axi_awready                  ),

        .dst_vld                        (awvalid                        ),
        .dst_dat                        (awaddr                         ),
        .dst_rdy                        (awready                        )
);

reg_slicer #(
        .RS_MODE                        ("BID_RS_HALF_RATE"             ),	// timing optimization for all input, but half rate pipeline
        .DAT_BW                         (DATA_BW                        )
) u_wdata ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .src_vld                        (s_axi_wvalid                   ),
        .src_dat                        (s_axi_wdata                    ),
        .src_rdy                        (s_axi_wready                   ),

        .dst_vld                        (wvalid                         ),
        .dst_dat                        (wdata                          ),
        .dst_rdy                        (wready                         )
);

assign ar_req = arvalid &          (~rvalid);
assign aw_req = awvalid & wvalid & (~bvalid);

assign idle = (~psel);
assign sch_en  = idle & (ar_req | aw_req);
assign apb_end = penable & pready;
always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        psel <=`U_DLY 1'b0;
    end else begin
        if (sch_en==1'b1)
            psel <=`U_DLY 1'b1;
        else if (apb_end==1'b1)
            psel <=`U_DLY 1'b0;
        else
            ;
    end
end

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        penable <=`U_DLY 1'b0;
    end else begin
        if ((psel==1'b1) && (penable==1'b0))
            penable <=`U_DLY 1'b1;
        else if (apb_end==1'b1)
            penable <=`U_DLY 1'b0;
        else if ((psel==1'b0) && (penable==1'b1))
            penable <=`U_DLY 1'b0;
        else
            ;
    end
end

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        paddr  <=`U_DLY {ADDR_BW{1'b0}};
        pwdata <=`U_DLY {DATA_BW{1'b0}};
        pwrite <=`U_DLY 1'b0;
    end else begin
        if ((idle==1'b1) && (ar_req==1'b1)) begin           // read higher priority
            paddr  <=`U_DLY araddr;
            pwrite <=`U_DLY 1'b0;
        end else if ((idle==1'b1) && (aw_req==1'b1)) begin  // write lower priority
            paddr  <=`U_DLY awaddr;
            pwdata <=`U_DLY wdata;
            pwrite <=`U_DLY 1'b1;
        end else
            ;
    end
end

assign arready = psel & (~penable) & (~pwrite); // arvalid can't fall before arready rise
assign awready = psel & (~penable) &   pwrite ; // awvalid can't fall before awready rise
assign wready  = psel & (~penable) &   pwrite ; // wvalid can't fall before wready rise

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        bvalid <=`U_DLY 1'b0;
    end else begin
        if ((psel==1'b1) && (penable==1'b0) && (pwrite==1'b1))
            bvalid <=`U_DLY 1'b1;
        else if ((bvalid==1'b1) && (bready==1'b1))
            bvalid <=`U_DLY 1'b0;
        else
            ;
    end
end

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        rvalid <=`U_DLY 1'b0;
        rdata  <=`U_DLY {DATA_BW{1'b0}};
    end else begin
        if ((penable==1'b1) && (pwrite==1'b0) && (pready==1'b1)) begin
            rvalid <=`U_DLY 1'b1;
            rdata  <=`U_DLY prdata;
        end else if ((rvalid==1'b1) && (rready==1'b1))
            rvalid <=`U_DLY 1'b0;
        else
            ;
    end
end

assign s_axi_bresp  = 2'b0;     // OK response
assign s_axi_bvalid = bvalid;
assign bready = s_axi_bready;

assign s_axi_rdata  = rdata;
assign s_axi_rresp  = 2'b0;     // OK response
assign s_axi_rvalid = rvalid;
assign rready = s_axi_rready;

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// synopsys translate_on
`endif

endmodule
